package handler

import (
	"context"
	"fmt"
	"github.com/golang/protobuf/proto"
	"github.com/valyala/fasthttp"
	"net/http"
	"strings"
	"time"

	context2 "kop/framework/context"
	"kop/framework/errors"
	"kop/libs/protobufrpc"
	"kop/modules/api/models"
	"kop/util/log"
)

// 从上下文取出令牌再从登陆服务器的REDIS取出令牌结构体
//func GetToken(ctx context.Context) (pb.Token, error) {
//
//	var md, ok = metadata.FromIncomingContext(ctx)
//	if !ok {
//		return pb.Token{}, errors.TokenError
//	}
//
//	// 客户端写入 token = "05c76b3f5847d9ca1e5c5855edb3ee31"
//	data, ok := md["token"]
//	if !ok || len(data) < 1 {
//		return pb.Token{}, errors.TokenError
//	}
//
//	return models.GetToken(data[0])
//}

func Handler(ctx *fasthttp.RequestCtx) {

	var path = string(ctx.Path())

	if strings.HasPrefix(path, "/ws/") {
		wsHandler(ctx)
		return
	}

	if string(ctx.Request.Header.ContentType()) != "application/protobuf" {
		ctx.SetStatusCode(http.StatusBadRequest)
		return
	}

	// 客户端写入 Token = "05c76b3f5847d9ca1e5c5855edb3ee31"
	var token = string(ctx.Request.Header.Peek("Token"))
	if len(token) == 0 {
		ctx.Error("token err", http.StatusUnauthorized)
		return
	}

	var names = strings.Split(path, "/")

	//  /service/func => (1)/(2)/(3)
	if len(names) != 3 {
		ctx.Error("path err", http.StatusUnauthorized)
		return
	}

	var newCtx = context.Background()

	switch path {

	case "/Time/Time":

		if _, err := models.GetUser(token); err != nil {

			ctx.Error(err.Error(), http.StatusUnauthorized)

		} else {
			_, _ = fmt.Fprintf(ctx, "%d", time.Now().Unix())
		}

		return

	case "/Connect/Connect":

		userID, err := models.GetUserID(token)
		if err != nil {

			user, err := models.GetUser(token)
			if err != nil {

				ctx.Error(err.Error(), http.StatusUnauthorized)
				return

			} else if user.UserID == 0 {

				ctx.Error("user.UserID == 0", http.StatusUnauthorized)
				return
			}

			userID = user.UserID
		}

		newCtx = context.WithValue(newCtx, "token", token)
		newCtx = context.WithValue(newCtx, "UserID", userID)

	default:

		var user, err = models.GetUser(token)
		if err != nil {
			ctx.Error(err.Error(), http.StatusUnauthorized)
			return
		}

		newCtx = context2.NewContext(user)
		newCtx = context2.WithUser(newCtx, user)
	}

	var beginTime = time.Now()

	defer rec(ctx)

	mess, req, err := protobufrpc.Run(names[1], names[2], newCtx, ctx.PostBody())
	if err != nil {

		log.Error("[%s]<%v> Error: %s", path, req, errors.Parse(err).Detail)

		ctx.Error(errors.Parse(err).Error(), http.StatusInternalServerError)
		return
	}

	ctx.SetContentType("application/protobuf")

	var body, _ = proto.Marshal(mess)
	_, _ = ctx.Write(body)

	log.Info("RPC [%s] Success, [%d] milliseconds", path, time.Now().Sub(beginTime).Milliseconds())
}

// recover
func rec(ctx *fasthttp.RequestCtx) {

	if r := recover(); r != nil {

		var err error

		switch e := r.(type) {
		case error:
			err = e
		case string:
			err = fmt.Errorf(e)
		default:
			err = fmt.Errorf("%v", e)
		}

		var srvErr = errors.Parse(err)
		srvErr.Detail = string(ctx.Path()) + ": " + srvErr.Detail
		ctx.Error(srvErr.Error(), http.StatusInternalServerError)

		log.Error("[%s] Error: %s", ctx.Path(), srvErr.Detail)
	}
}
